package com.zillion.framework.web.service;

import com.alibaba.fastjson2.JSONObject;
import com.zdww.utils.SM4Utils;
import com.zillion.common.core.domain.entity.SysRole;
import com.zillion.common.core.domain.entity.SysUser;
import com.zillion.common.core.domain.model.LoginUser;
import com.zillion.common.utils.SecurityUtils;
import com.zillion.common.utils.StringUtils;
import com.zillion.common.utils.http.HttpUtils;
import com.zillion.system.mapper.SysUserMapper;
import com.zillion.system.service.ISysUserService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * SSO 用户服务类
 */
@Service
public class SsoUserService {

    //根据appmark获取授权令牌
    private static final String GET_ACCESS_TOKEN_URL = "https://zwfw.gansu.gov.cn/api/sso/app/getAccessToken";

    //根据授权令牌获取ticket票据
    private static final String GET_TICKET_URL = "https://zwfw.gansu.gov.cn/api/sso/genCityTicket";

    //根据票据获取用户令牌 token
    private static final String GET_TOKEN_SNO_URL = "https://zwfw.gansu.gov.cn/api/sso/V2/getGsCityTokenWithTicket";

    // 获取自然人(个人)登录信息的链接地址
    private static final String GET_NATURAL_INFO_URL = "https://zwfw.gansu.gov.cn/api/sso/getNaturalInfoByToken";

    // 获取法人(单位)登录信息的链接地址
    private static final String GET_CORP_INFO_URL = "https://zwfw.gansu.gov.cn/api/sso/getCorpInfoByToken";

    // appmark,由省政府提供
    private static final String APP_MARK = "lzxqcyryjktipt";

    // 加密的秘钥,由省政府接口给
    private static final String SECRET_KEY = "F4SPBPHC4NJMTI2U";

    private static final Logger log = LoggerFactory.getLogger(SsoUserService.class); // 日志

    @Autowired
    private ISysUserService sysUserService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private SysLoginService loginService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private SysUserMapper sysUserMapper;


    /**
     * 使用授权码获取ticket票据
     *
     * @param accessToken 授权令牌-甘肃省接口获取
     * @param authCode    授权码-登录链接重定向url中获取
     * @return 票据
     */
    public String getTicketByAuthCode(String accessToken, String authCode) {

        // 设置header参数
        Map<String, String> headers = new HashMap<>();
        String authorization = "Bearer " + accessToken;
        headers.put("Authorization", authorization);

        // 对param参数进行加密
        String encryptAuthCode = "";
        try {
            // 对方指定的sm4EncryptECB加密
            encryptAuthCode = SM4Utils.sm4EncryptECB(authCode, SECRET_KEY);
        } catch (Exception e) {
            throw new RuntimeException("sm4EncryptECB()加密错误：" + e);
        }

        // 生成最终的请求地址
        String url = GET_TICKET_URL + "?authCode=" + encryptAuthCode;

        // 发送get请求
        String ticket = "";
        String response = HttpUtils.sendGetWithHeaders(url, headers);
        if (!response.isEmpty()) {
            JSONObject responseObj = JSONObject.parseObject(response);
            String code = responseObj.getString("code");
            if ("6000".equals(code)) {
                ticket = responseObj.getString("data");
            } else {
                log.error("获取 ticket 失败：{}", response);
                throw new RuntimeException("获取 ticket 失败");
            }
        }
        return ticket;
    }

    /**
     * 获取 TokenSNO
     *
     * @param accessToken 根据接口获取到的授权令牌
     * @param ticket      根据接口获得的票据ticket
     * @return TokenSNO
     */
    public String getTokenSNOByTicket(String accessToken, String ticket) {

        // 设置header参数
        Map<String, String> headers = new HashMap<>();
        String authorization = "Bearer " + accessToken;
        headers.put("Authorization", authorization);

        // 对param参数进行加密
        String encryptTicket = "";
        try {
            // 对方指定的sm4EncryptECB加密
            encryptTicket = SM4Utils.sm4EncryptECB(ticket, SECRET_KEY);
        } catch (Exception e) {
            throw new RuntimeException("sm4EncryptECB()加密错误：" + e);
        }

        // 生成最终的请求地址
        String url = GET_TOKEN_SNO_URL + "?ticketNo=" + encryptTicket;

        // 发送post请求
        String TokenSNO = "";
        String response = HttpUtils.sendPostWithHeaders(url, "", headers);
        if (!response.isEmpty()) {
            JSONObject responseObj = JSONObject.parseObject(response);
            String code = responseObj.getString("code");
            if ("6000".equals(code)) {
                TokenSNO = responseObj.getString("data");
            } else {
                log.error("获取 TokenSNO 失败：{}", response);
                throw new RuntimeException("获取 TokenSNO 失败");
            }
        }
        return TokenSNO;
    }

    /**
     * 使用 ticket + authCode 获取用户信息，并登录或注册本地账户
     *
     * @param authCode    认证码-甘肃省接口获取
     * @param ticket      票据-甘肃省接口获取
     * @param tokenSNO    认证码-甘肃省接口获取
     * @param loginType   自然人登录/法人登录
     * @param accessToken 自然人登录/法人登录的授权令牌
     * @return 登录结果
     */
    public String loginWithTicketAndAuthCode(String ticket, String authCode, String tokenSNO, String accessToken, String loginType) {
        log.info("获得的参数==ticket:{}/authCode:{}", ticket, authCode);
        String identifyCode = ""; // 获取用户信息中的唯一标识
        String username = ""; // 获取用户信息中的用户名
        String realName = ""; // 获取用户信息中的真实姓名
        String password = "";// 登录密码

        // 对param参数进行加密
        String encryptTokenSNO = "";
        String encryptAuthCode = "";
        try {
            // 对方指定的sm4EncryptECB加密
            encryptTokenSNO = SM4Utils.sm4EncryptECB(tokenSNO, SECRET_KEY);
            encryptAuthCode = SM4Utils.sm4EncryptECB(authCode, SECRET_KEY);
            log.info("sm4EncryptECB()加密结果==tokenSNO:{}/authCode:{}", encryptTokenSNO, encryptAuthCode);
        } catch (Exception e) {
            throw new RuntimeException("sm4EncryptECB()加密错误：" + e);
        }

        // 设置header参数
        Map<String, String> headers = new HashMap<>();
        String authorization = "Bearer " + accessToken;
        headers.put("Authorization", authorization);

        // 调用省平台接口获取用户信息
        String url = "";
        if ("perLogin".equals(loginType)) {
            // 获取自然人(个人用户)登录信息链接
            url = GET_NATURAL_INFO_URL + "?tokenSNO=" + encryptTokenSNO + "&authCode=" + encryptAuthCode;
        } else if ("corLogin".equals(loginType)) {
            // 获取法人(单位)登录信息链接
            url = GET_CORP_INFO_URL + "?tokenSNO=" + encryptTokenSNO + "&authCode=" + encryptAuthCode;
        }

        // 发送get请求
        String response = "";
        try {
            log.info("获取用户信息链接:{}/header:{}", url, headers);
            response = HttpUtils.sendGetWithHeaders(url, headers);
            log.info("获取用户信息结果:{}", response);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 返回结果处理
        if (!response.isEmpty()) {
            JSONObject responseObject = JSONObject.parseObject(response);
            // 获取返回code,6000代表成功,6001代表失败
            String code = responseObject.getString("code").trim();
            if ("6001".equals(code)) {
                String message = responseObject.getString("message").trim();
                // 如果message返回"请登录法人用户",那么就说明是企业账号,转为获取法人登录信息
                if ("请登录个人用户".equals(message)) {
                    String corUrl = GET_CORP_INFO_URL + "?tokenSNO=" + encryptTokenSNO + "&authCode=" + encryptAuthCode;
                    String corResponse = HttpUtils.sendGetWithHeaders(corUrl, headers);
                    if (!corResponse.isEmpty()) {
                        JSONObject corResponseObj = JSONObject.parseObject(corResponse);
                        String corCode = corResponseObj.getString("code");
                        if ("6000".equals(corCode)) {
                            JSONObject data = corResponseObj.getJSONObject("data");
                            identifyCode = data.getString("corpCertNum"); // 企业机构代码
                            username = data.getString("loginName"); // 法人登录名称
                            realName = data.getString("corpName"); // 机构名称
                            password = identifyCode.substring(identifyCode.length() - 6);
                            loginType = "corLogin";
                        } else {
                            log.error("无法从返回的ticket和authCode 中获取企业用户信息{}", responseObject.toJSONString());
                            throw new RuntimeException("无法从返回的ticket和authCode 中获取企业用户信息");
                        }
                    }
                }
            } else if ("6000".equals(code)) {
                JSONObject data = responseObject.getJSONObject("data");
                if ("perLogin".equals(loginType)) {
                    identifyCode = data.getString("userCertNum"); // 身份证号
                    username = data.getString("loginName"); // 用户登录省平台账号
                    realName = data.getString("userName"); // 用户真实姓名
                    password = identifyCode.substring(identifyCode.length() - 6);
                } else if ("corLogin".equals(loginType)) {
                    identifyCode = data.getString("corpCertNum"); // 企业机构代码
                    username = data.getString("loginName"); // 法人登录名称
                    realName = data.getString("corpName"); // 机构名称
                    password = identifyCode.substring(identifyCode.length() - 6);
                }
            }
        }

        // 查询本地用户是否存在
        SysUser user = sysUserService.selectUserByIdentifyCode(identifyCode);

        // 用户不存在，创建新用户
        if (!StringUtils.isNotNull(user)) {
            user = new SysUser();
            user.setUserName(username);
            user.setNickName(realName);
            if ("perLogin".equals(loginType)) {
                user.setUserType("01"); // 默认用户类型（00系统用户,01个人用户,02企业用户）
            } else if ("corLogin".equals(loginType)) {
                user.setUserType("02"); // 默认用户类型（00系统用户,01个人用户,02企业用户）
            }
            user.setPassword(SecurityUtils.encryptPassword(password)); // 设置密码为身份证\企业机构代码后六位
            user.setIdentifyCode(identifyCode);
            user.setStatus("0"); // 正常状态
            user.setRoleIds(new Long[]{102L}); // 设置角色赋予权限
            user.setPostIds(new Long[]{5L}); // 设置角色身份
            sysUserService.insertUser(user); // 插入用户
        }

        // 构建 LoginUser 并生成 Token
        LoginUser loginUser = new LoginUser();
        Long userId = user.getUserId();
        Set<String> permissions = sysUserMapper.selectUserPermissionsByUserId(userId);
        // 设置角色
        List<SysRole> roles = user.getRoles();
        if (!CollectionUtils.isEmpty(roles)) {
            roles.get(0).setPermissions(permissions);
        }
        // 设置权限
        loginUser.setUser(user);
        loginUser.setPermissions(permissions);
        loginUser.setUserId(userId);
        loginUser.setDeptId(user.getDeptId());

        // 跳过密码校验
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                loginUser, null, loginUser.getAuthorities()
        );
        SecurityContextHolder.getContext().setAuthentication(authentication);

        // 生成登录日志
        loginService.recordLoginInfo(loginUser.getUserId());
        // 生成 Token
        return tokenService.createToken(loginUser);
    }

    /**
     * 获取 AccessToken
     *
     * @return AccessToken
     */
    public String getAccessTokenByAppMark() {
        // 获取 AccessToken
        String url = GET_ACCESS_TOKEN_URL + "?appmark=" + APP_MARK;
        String response = HttpUtils.sendGet(url);
        if (!response.isEmpty()) {
            JSONObject responseObj = JSONObject.parseObject(response);
            String code = responseObj.getString("code");
            if ("200".equals(code)) {
                JSONObject data = responseObj.getJSONObject("data");
                return data.getString("token");
            } else {
                log.error("获取 AccessToken 失败：{}", response);
                throw new RuntimeException("获取 AccessToken 失败");
            }
        } else {
            log.error("响应为空，获取 AccessToken 失败");
            throw new RuntimeException("响应为空，获取 AccessToken 失败");
        }
    }

}
